/*
  GT20L16S1Y 供电电压为3.3V 引脚电平兼容5V,所以兼容Arduino
  GB2312标准共收录6763个汉字
  汉字范围:
  第16区 B0A0 ->  B0FF
  第17区 B1A0 ->  B1FF
  第18区 B2A0 ->  B2FF
          .
          .
          .
  第85区 F5A0 ->  F5FF
  第86区 F6A0 ->  F6FF
  第87区 F7A0 ->  F7FF
*/
#include <Arduino.h>
#include <U8g2lib.h>
#include <Wire.h>
#include <SPI.h>

//片选引脚
#define CS 49

//屏幕 使用硬件I2C 引脚根据你使用的板子连接
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/U8X8_PIN_NONE);

//将文字存储到Flash中，节约RAM
const unsigned char A[10] PROGMEM = { 0x47, 0x54, 0x32, 0x30, 0x4C, 0x31, 0x36, 0x53, 0x31, 0x59 };  //GT20L16S1Y
const unsigned char B[16] PROGMEM = { 0xF7, 0xE7, 0xF7, 0xE8, 0xF7, 0xE9, 0xF7, 0xEA, 0xF7, 0xEB, 0xF7, 0xEC, 0xF7, 0xED, 0xF7, 0xEE };
const unsigned char C[16] PROGMEM = { 0xF7, 0xDF, 0xF7, 0xE0, 0xF7, 0xE1, 0xF7, 0xE2, 0xF7, 0xE3, 0xF7, 0xE4, 0xF7, 0xE5, 0xF7, 0xE6 };

void setup() {

  //初始化
  u8g2.begin();
  SPI.begin();
  pinMode(CS, OUTPUT);

  //显示Flash中的汉字
  u8g2.clearBuffer();
  displayStr_flash(0, 0, 10, A);
  displayStr_flash(0, 16, 16, B);
  displayStr_flash(0, 32 + 5, 16, C);
  u8g2.sendBuffer();
  delay(1000);
}

void loop() {

  //测试
  test_all_char();
  
}

//测试所有汉字
void test_all_char() {
  unsigned char str[2] = { 0xb0, 0xa0 };
  while (1) {
    u8g2.clearBuffer();
    for (int i = 0; i < 4; i++) {
      for (int p = 0; p < 8; p++) {
        displayStr_ram(p * 16, i * 16, 2, str);
        str[1] += 0x01;
        if (str[1] == 0xff) {
          str[0] += 0x01;
          str[1] = 0xa1;
        };
        if (str[0] == 0xf7) { str[0] = 0xb0; };
      }
    }
    u8g2.sendBuffer();
    delay(100);
  }
}

long getAddr(uint16_t str) {
  byte hb = (byte)((str & 0xff00) >> 8);
  byte lb = (byte)(str & 0x00ff);
  // Serial.print("HB ");
  // Serial.print(hb, HEX);
  // Serial.print(" LB ");
  // Serial.println(lb, HEX);
  long Address;
  if (hb > 0xA0 && lb > 0xA0)  //is GB2312
  {
    long BaseAdd = 0;
    if (hb == 0xA9 && lb >= 0xA1) {
      Address = (282 + (lb - 0xA1));  //8位机在此出现了溢出，所以分三步
      Address = Address * 32;
      Address += BaseAdd;
    } else if (hb >= 0xA1 && hb <= 0xA3 && lb >= 0xA1) {
      Address = ((hb - 0xA1) * 94 + (lb - 0xA1));
      Address = Address * 32;
      Address += BaseAdd;
    } else if (hb >= 0xB0 && hb <= 0xF7 && lb >= 0xA1) {
      Address = ((hb - 0xB0) * 94 + (lb - 0xA1) + 846);
      Address = Address * 32;
      Address += BaseAdd;
    }
  } else {  //is ASCII
    long BaseAdd = 0x03b7c0;
    if (lb >= 0x20 && lb <= 0x7E) {
      Address = (lb - 0x20) * 16 + BaseAdd;
    }
  }
  return Address;
}

byte revBit(byte input) {
  byte output = 0;
  for (int i = 0; i < 8; i++) {
    if (input & (1 << i))
      output |= 1 << (7 - i);
  }
  return output;
}

void fetchBitmap16(long address, byte *p) {
  byte hb = (byte)((address & 0x00ff0000) >> 16);
  byte mb = (byte)((address & 0x0000ff00) >> 8);
  byte lb = (byte)(address & 0x000000ff);
  digitalWrite(CS, LOW);  //选通字库
  SPI.transfer(0x0b);     //移入命令字
  SPI.transfer(hb);       //移入地址次高位
  SPI.transfer(mb);       //移入地址次低位
  SPI.transfer(lb);       //移入地址最低位
  SPI.transfer(0x8d);     //移入dummy byte 可以为任意值
  for (int i = 0; i < 16; i++) {
    byte buf = SPI.transfer(0x00);  //读出16bytes
    Serial.print(buf, HEX);
    p[i] = revBit(buf);
  }
  Serial.println();
  digitalWrite(CS, HIGH);  //片选关
}

void fetchBitmap32(long address, byte *p) {
  byte hb = (byte)((address & 0x00ff0000) >> 16);
  byte mb = (byte)((address & 0x0000ff00) >> 8);
  byte lb = (byte)(address & 0x000000ff);
  digitalWrite(CS, LOW);  //选通字库
  SPI.transfer(0x0b);     //移入命令字
  SPI.transfer(hb);       //移入地址次高位
  SPI.transfer(mb);       //移入地址次低位
  SPI.transfer(lb);       //移入地址最低位
  SPI.transfer(0x8d);     //移入dummy byte 可以为任意值

  for (int i = 0; i < 32; i++) {
    byte buf = SPI.transfer(0x00);  //读出32bytes
    p[i] = revBit(buf);
  }
  digitalWrite(CS, HIGH);  //片选关
}

void drawV8P(int x, int y, byte b) {
  if ((b & 0x80) == 0x80)
    u8g2.drawPixel(x, y);
  if ((b & 0x40) == 0x40)
    u8g2.drawPixel(x, y + 1);
  if ((b & 0x20) == 0x20)
    u8g2.drawPixel(x, y + 2);
  if ((b & 0x10) == 0x10)
    u8g2.drawPixel(x, y + 3);
  if ((b & 0x08) == 0x08)
    u8g2.drawPixel(x, y + 4);
  if ((b & 0x04) == 0x04)
    u8g2.drawPixel(x, y + 5);
  if ((b & 0x02) == 0x02)
    u8g2.drawPixel(x, y + 6);
  if ((b & 0x01) == 0x01)
    u8g2.drawPixel(x, y + 7);
}

//ASCII是8x16
void draw8x16(int x, int y, byte *b) {
  int i;
  for (i = 0; i < 8; i++) {
    drawV8P(x + i, y, b[i]);
  }
  for (i = 0; i < 8; i++) {
    drawV8P(x + i, y + 8, b[i + 8]);
  }
}

//汉字是16x16
void draw16x16(int x, int y, byte *b) {
  int i;
  for (i = 0; i < 16; i++) {
    drawV8P(x + i, y, b[i]);
  }
  for (i = 0; i < 16; i++) {
    drawV8P(x + i, y + 8, b[i + 16]);
  }
}

//显示在Flash(PROGMEM)中的汉字
void displayStr_flash(int x, int y, int len, unsigned char *c) {
  int i;
  for (i = 0; i < len; i++) {
    if (pgm_read_byte(c + i) > 0xA0)  //GB2312
    {
      int code = (int)(pgm_read_byte(c + i) * 256) + (int)pgm_read_byte(c + i + 1);
      byte bmp[32];
      fetchBitmap32(getAddr(code), bmp);
      draw16x16(x, y, bmp);
      x += 16;
      i++;
    } else {  //ASCII
      int code = (int)pgm_read_byte(c + i);
      byte bmp[16];
      fetchBitmap16(getAddr(code), bmp);
      draw8x16(x, y, bmp);
      x += 8;
    }
  }
}

//显示在RAM中的汉字
void displayStr_ram(int x, int y, int len, unsigned char *c) {
  int i;
  for (i = 0; i < len; i++) {
    if (c[i] > 0xA0)  //is GB2312
    {
      int code = (int)(c[i] * 256) + (int)c[i + 1];
      byte bmp[32];
      fetchBitmap32(getAddr(code), bmp);
      draw16x16(x, y, bmp);
      x += 16;
      i++;
    } else {  //is ASCII
      int code = (int)c[i];
      byte bmp[16];
      fetchBitmap16(getAddr(code), bmp);
      draw8x16(x, y, bmp);
      x += 8;
    }
  }
}

//显示汉字，在坐标中间显示
void displayStrC(int row, int len, unsigned char *c) {
  int x = 64 - ((len * 8) / 2);
  displayStr_flash(x, row, len, c);
}